/* 
Batch Apex: Phase 2 OOR
Author: Bibhudutta Mishra
Purpose: SCRIPT to check all primary Leads as MASTER and duplicates 
         This should be run once in production for the past leads
         Due to the huge amount of leads to be processed , the SCRIPT to update past leads cannot be simply an annonymous block
Version: 1.0  Date: 01/09/2011  
Version: 1.1  Date: 20/10/2011
*/
global class OORMasterLeadCheck implements Database.Batchable<sObject>{
    Public String query;    
            global Database.QueryLocator start(Database.BatchableContext BC){
                //Query = 'select id,Master__c,DupingAccountID__c,duplicate_owner__c,Unique_lead_list__c,ISCONVERTED from Lead where ISCONVERTED=FALSE AND recordtypeId = 'Local Advertiser';
                return Database.getQueryLocator(query);
            }
            global void execute(Database.BatchableContext BC, List<sObject> scope){
                 Set<id> LeadIds = new Set<id>();
                 Set<id> Leadconv = new Set<id>();
                 List<Lead> SecLeads = new List<Lead>();
                 List<Lead> Leads = new List<Lead>();
                 Set<ID> bizNameLeads = new Set<ID>();
                 Set<ID>  phoneNumLeads = new Set<ID>();
                 set<string> bizaccNames = new Set<string>();
                 Set<string> bizNames = new Set<string>();
                 Set<string> oldbizNames = new Set<string>();
                 Set<string>  phones= new Set<string>(); 
                 Set<string>  oldphones= new Set<string>();
                 ID LocalAdsLeadRecordType = RecordTypeHelper.GetRecordTypeID('Lead', 'Local Advertiser');
                 ID LocalAdsAccountRecordType = RecordTypeHelper.GetRecordTypeID('Account', 'Local OOR Customer');
                    
                    for(sobject s : scope){
                            Lead a = (Lead)s;
                            LeadIds.add(a.id);
                            Leads.add(a);
             //Comment 1: All the Lead's Id, Company, Phone, City, State in a List               
                bizNameLeads.add(a.Id); 
                bizNames.add(a.Company.toLowerCase());
                phoneNumLeads.add(a.Id);
                If(a.cleanphonetext__c != Null){                       
                phones.add(a.cleanphonetext__c);
                }
                else{
                phones.add(a.Phone);
                }
                bizaccNames.add(a.Company+a.City+a.State); 
                  
                }
                system.debug('########'+Leads.size());
                if(Limits.getHeapSize() < 0.81*Limits.getLimitHeapSize()){
              // Comment 2:Calling the Update Lead Code for the Duplicate and Master check 
                 doUpdatebatch(bizNameLeads, bizNames, phoneNumLeads, phones, LocalAdsLeadRecordType,LocalAdsAccountRecordType,bizaccNames,oldphones,oldbizNames);       
                }      

             }
             public static void doUpdatebatch(Set<ID> leadBusNames, Set<string> bizNames, Set<ID> phoneNumLeads,Set<string> phones, ID LeadRecordType ,ID AccountRecordType,set<string> bizaccNames,set<string> oldphoneleads,set<string> oldbizNames){
             
           OORDuplicateCheck.setRecursive(true);      
                FindMatchingLeadsBatch( phones,  bizNames ,oldphoneleads,oldbizNames,LeadRecordType );
                FindAccountswithNameMatchBatch( bizaccNames ,leadBusNames, LeadRecordType , AccountRecordType);                 
           OORDuplicateCheck.setRecursive(false);
          }
          //find all matching leads
      private static void FindMatchingLeadsBatch(Set<string> phones, Set<string> bizNames ,Set<string> oldphoneleads,Set<string> oldbizNames,ID LeadRecordType )
      {
            FindLeadswithPhoneMatchBatch(phones,LeadRecordType);
            FindLeadswithNameMatchBatch(bizNames,LeadRecordType);
           
      }
      //find leads with matching Phone
      private static void FindLeadswithPhoneMatchBatch(Set<string> phones,ID LeadRecordType)
      {
            Map<String,List<Lead> > phoneLeadsMap  = new Map<String, List<Lead>>(); 
                        
            List<Lead> dupingLeads =   [        SELECT ID, IsDupe__C,Master__c, DupingLeadID__c,duplicate__c,owner.name, Phone,CleanPhoneText__c, OwnerId 
                                                FROM     Lead 
                                                WHERE    cleanphonetext__C IN :phones and 
                                                         RecordTypeid = :LeadRecordType and 
                                                         IsConverted = false 
                                                ORDER BY createddate ASC  ];
                 
            //create a  phone->leads map
            for(Lead lead : dupingLeads)    {               
               if(lead.cleanphonetext__C !=null && !phoneLeadsMap.containsKey(lead.cleanphonetext__C)) {
                  //add this new company list                 
                  phoneLeadsMap.put(lead.cleanphonetext__C, new List<Lead>{lead});
               }
               else {                
                 phoneLeadsMap.get(lead.cleanphonetext__C).add(lead);
               }
            } 
            
              DedupeLeadsBatch(phones, phoneLeadsMap,'Phone' );               
      }
      //find leads with matching Name
      private static void FindLeadswithNameMatchBatch(Set<string> leadBusNames,ID LeadRecordType)
      {
            Map<String,List<Lead> > businessLeadsMap  = new Map<String, List<Lead>>();          
            List<Lead> dupingLeads =   [        SELECT ID, IsDupe__C, DupingLeadID__c,owner.name,Company, OwnerId 
                                                FROM     Lead 
                                                WHERE    Company IN :leadBusNames and 
                                                         RecordTypeid = :LeadRecordType and 
                                                         IsConverted = false                                                         
                                                ORDER BY createddate ASC  ];
                 
            //create a  business  name->leads map
            for(Lead lead : dupingLeads)    {                               
                   if(lead.Company != null && !businessLeadsMap.containsKey(lead.Company.toLowerCase())) {
                      //add this new company list                 
                          businessLeadsMap.put(lead.Company.toLowerCase(), new List<Lead>{lead});
                   }
                   else {                  
                          businessLeadsMap.get(lead.Company.toLowerCase()).add(lead);
                   }                
            }
            
            DedupeLeadsBatch(leadBusNames, businessLeadsMap,'Name' );            
      }
      //mark leads as dupe
      private static void DedupeLeadsBatch(Set<string> items,Map<String,List<Lead>> itemMap,string dupeType )
      {
         //now mark all dup leads as duplicate
            List<Lead> batchOfLeads = new List<Lead>();
               
            for (string leadItem: items)    {
                //if thre are any duplicates only!                                                                                                                                 
                if (itemMap != null && itemMap.containsKey(leadItem) && itemMap.get(leadItem).size() > 1) {         
                        //take the current phone 
                        //update all phones ,first one as primary and rest all as duping leads
                         ID primaryDupingLead = null ;  
                         String primaryLeadOwner = null;
                         string uniquefield = null;  
                         //integer count = 0; 
                         for (Lead lead2update: itemMap.get(leadItem)){
                                     
                                     
                                                                 
                                            lead2update.IsDupe__C = true;
                                            lead2update.Duplicate_Status__C = 'Duplicate';
                                            lead2update.Duplicate_Reason_ID__c = (dupeType=='Phone' ? 1 : 2) ;                                            
                                            lead2update.Duplicate_Reason_Desc__c = dupeType;
                                            lead2update.duplicate__c = true;
                                            lead2update.Master__c=True;
                                             
                                            
                                                 
                                                                                                                                         
                                     //mark the first lead as primary dupe!                           
                                      if(primaryDupingLead == null){
                                         primaryDupingLead = lead2update.Id;
                                         primaryLeadOwner   =  lead2update.owner.name;
                                         uniquefield        =  lead2update.id;
                                         lead2update.Unique_lead_list__c = lead2update.id;
                                       
                                      }                                                           
                                       else{
                                          if(primaryDupingLead != null)
                                               lead2update.DupingLeadID__c = primaryDupingLead ;
                                               lead2update.Duplicate_owner__c = primaryLeadOwner;
                                               lead2update.Unique_lead_list__c = uniquefield;
                                               lead2update.Master__c=False;
                                           
                                       }     
                                                                           
                                batchOfLeads.add(lead2update);
                               } //for           
                        } //if 
                } //for          
            try{
                update batchOfLeads;
            } catch (DMLException e){
                                            
            }
            
            
      }
      //find accounts with matching Name
      private static void FindAccountswithNameMatchBatch(Set<string> leadBusNames ,Set<ID> leadBusIds,ID LeadRecordType ,ID AccountRecordType)
        {
            //get the newly inserted records..
            List<Lead> leadBusinessNameList =[ SELECT ID, IsDupe__C,OOR_LeadCityState__c, DupingLeadID__c,Duplicate__c, DupingAccountID__c, Company, OwnerId FROM Lead WHERE ID IN :leadBusIds and RecordTypeid = :LeadRecordType and IsDupe__C =FALSE and IsConverted =false order by createddate asc  ];
             
            Map<String,List<Account> > businessAccMap  = new Map<String, List<Account>>();
            
            //get the Accounts with same biz names 
            List<Account> dupingAccounts = [ SELECT ID,Name,Owner.Name,OOR_AccountCityState__c FROM Account WHERE OOR_AccountCityState__c IN :leadBusNames and RecordTypeid = :AccountRecordType];        
            //create a  business  name->leads map
            for(Account acc : dupingAccounts)       {
                if(acc.Name != null){                                      
                       if(!businessAccMap.containsKey(acc.Name.toLowerCase())) {
                          //add this new company list                         
                          businessAccMap.put(acc.OOR_AccountCityState__c.toLowerCase() , new List<Account>{acc});
                       }
                       else {                                     
                          businessAccMap.get(acc.OOR_AccountCityState__c.toLowerCase()).add(acc);
                       }
                }
            }
            
            List<Lead> batchOfLeads = new List<Lead>();
            //go thru all dupe leads and mark as duplicate ,update the duping acc id
            if(businessAccMap !=null && businessAccMap.size() > 0  ){
                for (Lead newLeadCompany: leadBusinessNameList)     {                       
                    if (businessAccMap.containsKey(newLeadCompany.OOR_LeadCityState__c.toLowerCase())){          
                     for (Account dupingAcc: businessAccMap.get(newLeadCompany.OOR_LeadCityState__c.toLowerCase()))  {                                                                                 
                                //mark the first lead as duplicate
                                                                                           
                                    newLeadCompany.IsDupe__C = true;
                                    newLeadCompany.Duplicate_Status__C = 'Duplicate';
                                    newLeadCompany.Duplicate_Reason_ID__c = 2 ;
                                    newLeadCompany.Duplicate_Reason_Desc__c = 'Name';
                                    newLeadCompany.Duplicate__c = true;
                                    
                                                                                                                                                                                                    
                                 newLeadCompany.DupingAccountID__c = dupingAcc.ID ;
                                 newLeadCompany.Duplicate_Owner__c = dupingAcc.Owner.Name ;

                                 
                                 batchOfLeads.add(newLeadCompany);
                                 //assuming only one account will have this name
                                 break;                                  
                                                                                                
                        } 
                      }       
                    }
             
                try{
                update batchOfLeads;
                }   
                catch (DMLException e){     
                system.debug('EEEEEEE'+e);                 
                }
            
                                    
            } 
   } 
        global void finish(Database.BatchableContext BC){
                        }
    /* test Method*/  
      static testMethod void OORMasterLeadCheck() {
             List <Lead> leads1 = new List<Lead>();
             //List <Lead> leads2 = new List<Lead>();
             ID LocalAdsLeadRecordType = RecordTypeHelper.GetRecordTypeID('Lead', 'Local Advertiser');
             for(integer i = 0; i<200; i++){
                 Lead a = new Lead(FirstName='A',lastname='testLeads'+i,recordtypeId=LocalAdsLeadRecordType,company='testLead'+i,LeadSource='Internet');
                     leads1.add(a);
             }
             insert leads1;
             Test.startTest(); 
             OORMasterLeadCheck job = new OORMasterLeadCheck();
             job.query = 'select id,Master__c,DupingAccountID__c,duplicate_owner__c,Unique_lead_list__c,ISCONVERTED from Lead where name Like \'testLeads%\' ';
             Database.executeBatch(job,100);
             Test.stopTest();
             }
}